package com.smart.community.region.controller;

import cn.dev33.satoken.annotation.SaCheckPermission;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.smart.community.commons.annotation.ApiLog;
import com.smart.community.commons.enums.ApiLogOperationType;
import com.smart.community.commons.exception.BusinessException;
import com.smart.community.commons.result.Result;
import com.smart.community.commons.utils.SecurityUtils;
import com.smart.community.feign.property.service.DataScopeFeign;
import com.smart.community.feign.property.service.IPropertyCompanyCommunityFeign;
import com.smart.community.region.dto.ZoneQueryDTO;
import com.smart.community.region.dto.ZoneInfoUpdateDTO;
import com.smart.community.region.entity.Zone;
import com.smart.community.region.service.IZoneService;
import com.smart.community.region.vo.ZoneDetailVO;
import com.smart.community.region.vo.ZoneWithBuildingsVO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.util.List;

/**
 * 分区管理控制器
 * 
 * @author Wu.Liang
 * @since 2025-01-30
 * @version 1.0.0
 */
@Slf4j
@RestController
@RequestMapping("/region/zones")
@Tag(name = "分区管理", description = "分区管理相关接口")
@Validated
public class ZoneController {

    @Autowired
    private IZoneService zoneService;
    
    @Autowired
    private IPropertyCompanyCommunityFeign propertyCompanyCommunityFeign;

    @Autowired
    private DataScopeFeign dataScopeFeign;

    // ==================== 基础CRUD接口 ====================

    /**
     * 分页查询分区列表
     * 
     * @param page 分页参数
     * @param queryDTO 查询条件
     * @return 分区列表
     * @throws Exception 查询异常
     */
    @GetMapping
    @Operation(summary = "分页查询分区列表", description = "根据条件分页查询分区列表")
    @SaCheckPermission("region:zone:list")
    public Result<IPage<Zone>> getZonePage(
            @Parameter(description = "分页参数") Page<Zone> page,
            @Parameter(description = "查询条件") ZoneQueryDTO queryDTO) throws Exception {
        
        log.info("分页查询分区列表，页码：{}，大小：{}，查询条件：{}", page.getCurrent(), page.getSize(), queryDTO);
        
        IPage<Zone> result = zoneService.getZonePage(page, queryDTO);
        log.info("分页查询分区列表成功，总数：{}", result.getTotal());
        return Result.success(result);
    }

    /**
     * 根据ID查询分区详情
     * 
     * @param id 分区ID
     * @return 分区详情
     */
    @GetMapping("/{id}")
    @Operation(summary = "查询分区详情", description = "根据分区ID查询分区详细信息")
    @SaCheckPermission("region:zone:detail")
    public Result<ZoneDetailVO> getZoneDetail(@Parameter(description = "分区ID") @PathVariable Long id) throws Exception {
        log.info("查询分区详情，分区ID：{}", id);
        
        ZoneDetailVO zoneDetail = zoneService.getZoneDetail(id);
        if (zoneDetail == null) {
            log.warn("分区不存在，分区ID：{}", id);
            return Result.error("分区不存在");
        }
        
        log.info("查询分区详情成功，分区ID：{}", id);
        return Result.success(zoneDetail);
    }

    /**
     * 更新分区基本信息
     * 
     * @param id 分区ID
     * @param dto 更新数据
     * @return 更新结果
     */
    @PutMapping("/{id}/info")
    @Operation(summary = "更新分区基本信息", description = "更新分区的基本信息，如名称、编码、类型等")
    @SaCheckPermission("region:zone:edit")
    @ApiLog(logTitle = "更新分区基本信息", logType = 2, moduleName = "区域管理", operationType = ApiLogOperationType.UPDATE)
    public Result<String> updateZoneInfo(
            @Parameter(description = "分区ID") @PathVariable Long id,
            @Parameter(description = "更新数据") @Valid @RequestBody ZoneInfoUpdateDTO dto) throws Exception {
        log.info("更新分区基本信息，分区ID：{}，分区名称：{}", id, dto.getZoneName());
        
        // 获取当前分区信息以获取社区ID
        Zone existingZone = zoneService.getById(id);
        if (existingZone == null) {
            log.warn("分区不存在，分区ID：{}", id);
            return Result.error("分区不存在");
        }
        
        // 检查分区编码是否存在（在指定社区内，排除当前分区）
        if (zoneService.isZoneCodeExists(dto.getZoneCode(), existingZone.getCommunityId(), id)) {
            log.warn("分区编码已存在，分区编码：{}，社区ID：{}，排除ID：{}", dto.getZoneCode(), existingZone.getCommunityId(), id);
            return Result.error("该社区内分区编码已存在");
        }
        
        // 设置更新用户信息
        Long currentUserId = SecurityUtils.getCurrentUserId();
        
        boolean success = zoneService.updateZoneInfo(id, dto, currentUserId);
        if (success) {
            log.info("更新分区基本信息成功，分区ID：{}", id);
            return Result.success("更新成功");
        } else {
            log.error("更新分区基本信息失败，分区ID：{}", id);
            return Result.error("更新失败");
        }
    }

    /**
     * 创建新分区
     * 
     * @param zone 分区信息
     * @return 创建结果
     */
    @PostMapping
    @Operation(summary = "创建分区", description = "创建新的分区")
    @SaCheckPermission("region:zone:add")
    @ApiLog(logTitle = "创建分区", logType = 2, moduleName = "区域管理", operationType = ApiLogOperationType.ADD)
    public Result<Zone> createZone(@Parameter(description = "分区信息") @Valid @RequestBody Zone zone) throws Exception {
        log.info("创建分区，分区名称：{}，分区编码：{}，社区ID：{}", zone.getZoneName(), zone.getZoneCode(), zone.getCommunityId());
        
        // 数据权限验证
        validateCommunityPermission(zone.getCommunityId());
        
        // 检查分区编码是否存在（在指定社区内）
        if (zoneService.isZoneCodeExists(zone.getZoneCode(), zone.getCommunityId(), null)) {
            log.warn("分区编码已存在，分区编码：{}，社区ID：{}", zone.getZoneCode(), zone.getCommunityId());
            return Result.error("该社区内分区编码已存在");
        }
        
        // 根据社区ID获取物业公司ID
        if (zone.getCommunityId() == null) {
            log.error("创建分区失败，社区ID不能为空");
            return Result.error("社区ID不能为空");
        }
        
        Long propertyCompanyId = propertyCompanyCommunityFeign.getPropertyCompanyIdByCommunityId(zone.getCommunityId());
        if (propertyCompanyId == null) {
            log.error("创建分区失败，社区ID：{} 未关联任何物业公司", zone.getCommunityId());
            return Result.error("该社区未关联物业公司，无法创建分区");
        }
        
        // 设置物业公司ID
        zone.setPropertyCompanyId(propertyCompanyId);
        log.info("根据社区ID获取物业公司ID成功，社区ID：{}，物业公司ID：{}", zone.getCommunityId(), propertyCompanyId);
        
        // 设置创建用户信息
        Long currentUserId = SecurityUtils.getCurrentUserId();
        zone.setCreateBy(currentUserId);
        zone.setUpdateBy(currentUserId);
        
        boolean success = zoneService.save(zone);
        if (success) {
            log.info("创建分区成功，分区ID：{}", zone.getId());
            // 返回包含分区ID的分区对象
            return Result.success(zone);
        } else {
            log.error("创建分区失败，分区名称：{}", zone.getZoneName());
            return Result.error("创建失败");
        }
    }

    /**
     * 更新分区信息
     * 
     * @param id 分区ID
     * @param zone 分区信息
     * @return 更新结果
     */
    @PutMapping("/{id}")
    @Operation(summary = "更新分区", description = "更新分区信息")
    @SaCheckPermission("region:zone:edit")
    @ApiLog(logTitle = "更新分区", logType = 2, moduleName = "区域管理", operationType = ApiLogOperationType.UPDATE)
    public Result<String> updateZone(
            @Parameter(description = "分区ID") @PathVariable Long id,
            @Parameter(description = "分区信息") @Valid @RequestBody Zone zone) throws Exception {
        
        log.info("更新分区，分区ID：{}，分区名称：{}", id, zone.getZoneName());
        
        // 数据权限验证
        validateZonePermission(id);
        
        zone.setId(id);
        
        // 检查分区编码是否存在（在指定社区内，排除当前分区）
        if (zoneService.isZoneCodeExists(zone.getZoneCode(), zone.getCommunityId(), id)) {
            log.warn("分区编码已存在，分区编码：{}，社区ID：{}，排除ID：{}", zone.getZoneCode(), zone.getCommunityId(), id);
            return Result.error("该社区内分区编码已存在");
        }
        
        // 设置更新用户信息
        Long currentUserId = SecurityUtils.getCurrentUserId();
        zone.setUpdateBy(currentUserId);
        
        boolean success = zoneService.updateById(zone);
        if (success) {
            log.info("更新分区成功，分区ID：{}", id);
            return Result.success("更新成功");
        } else {
            log.error("更新分区失败，分区ID：{}", id);
            return Result.error("更新失败");
        }
    }

    /**
     * 删除分区
     * 
     * @param id 分区ID
     * @return 删除结果
     */
    @DeleteMapping("/{id}")
    @Operation(summary = "删除分区", description = "删除指定分区")
    @SaCheckPermission("region:zone:delete")
    @ApiLog(logTitle = "删除分区", logType = 2, moduleName = "区域管理", operationType = ApiLogOperationType.DELETE)
    public Result<String> deleteZone(@Parameter(description = "分区ID") @PathVariable Long id) throws Exception {
        log.info("删除分区，分区ID：{}", id);
        
        // 数据权限验证
        validateZonePermission(id);
        
        boolean success = zoneService.removeById(id);
        if (success) {
            log.info("删除分区成功，分区ID：{}", id);
            return Result.success("删除成功");
        } else {
            log.error("删除分区失败，分区ID：{}", id);
            return Result.error("删除失败");
        }
    }

    // ==================== 业务查询接口 ====================

    /**
     * 查询社区分区列表
     * 
     * @param communityId 社区ID
     * @return 分区列表
     */
    @GetMapping("/community/{communityId}")
    @Operation(summary = "查询社区分区列表", description = "根据社区ID查询分区列表")
    @SaCheckPermission("region:zone:list")
    public Result<List<ZoneWithBuildingsVO>> getZonesByCommunity(@Parameter(description = "社区ID") @PathVariable Long communityId) throws Exception {
        log.info("查询社区分区列表，社区ID：{}", communityId);
        
        List<ZoneWithBuildingsVO> zones = zoneService.getByCommunityId(communityId);
        log.info("查询社区分区列表成功，社区ID：{}，分区数量：{}", communityId, zones.size());
        return Result.success(zones);
    }

    /**
     * 查询物业公司分区列表
     * 
     * @param propertyCompanyId 物业公司ID
     * @return 分区列表
     */
    @GetMapping("/property-company/{propertyCompanyId}")
    @Operation(summary = "查询物业公司分区列表", description = "根据物业公司ID查询分区列表")
    @SaCheckPermission("region:zone:list")
    public Result<List<Zone>> getZonesByPropertyCompany(@Parameter(description = "物业公司ID") @PathVariable Long propertyCompanyId) throws Exception {
        log.info("查询物业公司分区列表，物业公司ID：{}", propertyCompanyId);
        
        List<Zone> zones = zoneService.getByPropertyCompanyId(propertyCompanyId);
        log.info("查询物业公司分区列表成功，物业公司ID：{}，分区数量：{}", propertyCompanyId, zones.size());
        return Result.success(zones);
    }

    /**
     * 检查分区编码是否存在（在指定社区内）
     * 
     * @param zoneCode 分区编码
     * @param communityId 社区ID
     * @param excludeId 排除的分区ID
     * @return 是否存在
     */
    @GetMapping("/check-code")
    @Operation(summary = "检查分区编码", description = "检查分区编码是否已在指定社区内存在")
    @SaCheckPermission("region:zone:list")
    public Result<Boolean> checkZoneCode(
            @Parameter(description = "分区编码") @RequestParam String zoneCode,
            @Parameter(description = "社区ID") @RequestParam Long communityId,
            @Parameter(description = "排除的分区ID") @RequestParam(required = false) Long excludeId) throws Exception {
        
        log.info("检查分区编码，编码：{}，社区ID：{}，排除ID：{}", zoneCode, communityId, excludeId);
        
        boolean exists = zoneService.isZoneCodeExists(zoneCode, communityId, excludeId);
        log.info("检查分区编码完成，编码：{}，社区ID：{}，存在：{}", zoneCode, communityId, exists);
        return Result.success(exists);
    }

    /**
     * 验证社区权限
     * 
     * @param communityId 社区ID
     */
    private void validateCommunityPermission(Long communityId) throws Exception {
        Long currentUserId = SecurityUtils.getCurrentUserId();
        log.debug("验证用户权限，用户ID: {}, 社区ID: {}", currentUserId, communityId);
        
        try {
            // 1. 检查超级管理员权限
            Boolean isSuperAdmin = dataScopeFeign.isUserSuperAdmin(currentUserId);
            if (Boolean.TRUE.equals(isSuperAdmin)) {
                log.debug("用户{}为超级管理员，拥有所有权限", currentUserId);
                return;
            }
            
            // 2. 验证社区权限
            Boolean hasCommunityPermission = dataScopeFeign.hasCommunityPermission(currentUserId, communityId);
            if (!Boolean.TRUE.equals(hasCommunityPermission)) {
                log.warn("用户{}无权限访问社区{}", currentUserId, communityId);
                throw new BusinessException("无权限访问该社区");
            }
            
            log.debug("用户{}权限验证通过，社区ID: {}", currentUserId, communityId);
        } catch (BusinessException e) {
            // 业务异常直接抛出
            throw e;
        } catch (Exception e) {
            log.error("权限验证失败，用户ID: {}, 社区ID: {}, 错误: {}", currentUserId, communityId, e.getMessage());
            // 权限验证失败时，默认拒绝访问，确保安全性
            throw new BusinessException("权限验证失败，请稍后重试");
        }
    }

    /**
     * 验证分区操作权限
     * 
     * @param zoneId 分区ID
     */
    private void validateZonePermission(Long zoneId) throws Exception {
        Long currentUserId = SecurityUtils.getCurrentUserId();
        
        try {
            // 1. 检查超级管理员权限
            Boolean isSuperAdmin = dataScopeFeign.isUserSuperAdmin(currentUserId);
            if (Boolean.TRUE.equals(isSuperAdmin)) {
                log.debug("用户{}为超级管理员，拥有所有权限", currentUserId);
                return;
            }
            
            // 2. 获取分区信息
            Zone zone = zoneService.getById(zoneId);
            if (zone == null) {
                throw new BusinessException("分区不存在");
            }
            
            // 3. 验证社区权限
            Boolean hasCommunityPermission = dataScopeFeign.hasCommunityPermission(currentUserId, zone.getCommunityId());
            if (!Boolean.TRUE.equals(hasCommunityPermission)) {
                log.warn("用户{}无权限操作社区{}的分区", currentUserId, zone.getCommunityId());
                throw new BusinessException("无权限操作该分区");
            }
            
            log.debug("用户{}分区权限验证通过，分区ID: {}", currentUserId, zoneId);
        } catch (BusinessException e) {
            // 业务异常直接抛出
            throw e;
        } catch (Exception e) {
            log.error("分区权限验证失败，用户ID: {}, 分区ID: {}, 错误: {}", currentUserId, zoneId, e.getMessage());
            // 权限验证失败时，默认拒绝访问，确保安全性
            throw new BusinessException("权限验证失败，请稍后重试");
        }
    }
}
